﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HQHS.Library.Common.Colloection;
using HQHS.Library.Common.Threading;
using System.ComponentModel;

namespace HQHS.Library.Common.Caching
{
    public abstract class CacheBase<TKey, TValue>
    {
        private SynchronizedDictionary<TKey, CachedItemInfo<TKey, TValue>> _cache;
        private ICacheManager<TKey, TValue> _cacheManager;
        private KeyedReaderWriterLockSlim<TKey> _contentLoadLock;
        protected internal readonly string _cacheKey;

        protected CacheBase(string cacheKey)
        {
            this._cache = new SynchronizedDictionary<TKey, CachedItemInfo<TKey, TValue>>();
            this._contentLoadLock = new KeyedReaderWriterLockSlim<TKey>(LockRecursionPolicy.SupportsRecursion);
            this._cacheManager = this.GetCustomCacheManager(this._cache, this._contentLoadLock);
            this._cacheKey = cacheKey;
        }

        private CachedItemInfo<TKey, TValue> GetCachedItem(TKey key)
        {
            return this.GetCachedItem(key, this.LoadItem);
        }

        private CachedItemInfo<TKey, TValue> GetCachedItem(TKey key, LoadItemDelegate loadMethod)
        {
            CachedItemInfo<TKey, TValue> info = null;
            if (!this._cache.TryGetValue(key, out info))
            {
                this._cache.EnterWriteLock();
                try
                {
                    if (!this._cache.TryGetValue(key, out info))
                    {
                        info = new CachedItemInfo<TKey, TValue>(key);
                        this._cache[key] = info;
                    }
                }
                finally
                {
                    this._cache.ExitWriteLock();
                }
            }
            this._cacheManager.Refresh(ref info, loadMethod, this._contentLoadLock);
            info.LastRequestedTimestamp = DateTime.Now;
            return info;
        }

        protected virtual ICacheManager<TKey, TValue> GetCustomCacheManager(SynchronizedDictionary<TKey, CachedItemInfo<TKey, TValue>> elementCache, KeyedReaderWriterLockSlim<TKey> contentLoadLock)
        {
            throw new NotImplementedException();
        }

        protected TValue GetItem(TKey key)
        {
            return this.GetItem(key, this.LoadItem);
        }

        protected TValue GetItem(TKey key, LoadItemDelegate loadMethod)
        {
            return this.GetCachedItem(key, loadMethod).Value;
        }

        protected abstract object LoadItem(object key);
        protected void RefreshAll()
        {
            this._cacheManager.RefreshAll();
        }

        protected void RefreshItem(TKey key)
        {
            CachedItemInfo<TKey, TValue> info = null;
            this._contentLoadLock.EnterWriteLock(key);
            try
            {
                if (this._cache.TryGetValue(key, out info))
                {
                    info.LastRefreshTimestamp = DateTime.Now;
                    info.Value = (TValue)this.LoadItem(key);
                    info.IsLoaded = true;
                }
                else
                {
                    this.GetItem(key);
                }
            }
            finally
            {
                this._contentLoadLock.ExitWriteLock(key);
            }
        }

        protected void UncacheItem(TKey key)
        {
            this._contentLoadLock.EnterWriteLock(key);
            try
            {
                if (this._cache.ContainsKey(key))
                {
                    this._cache.Remove(key);
                }
            }
            finally
            {
                this._contentLoadLock.ExitWriteLock(key);
            }
        }
    }
}
